package com.tencent.sr.iris.activity.service.event.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.google.common.util.concurrent.RateLimiter;
import com.tencent.sr.iris.activity.common.config.DependencyConfig;
import com.tencent.sr.iris.activity.common.config.IrisThreadPoolConfig;
import com.tencent.sr.iris.activity.common.util.TimeUtils;
import com.tencent.sr.iris.activity.dependency.qufa.GroupTaskDependency;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityEventSourceEnum;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityEventTypeEnum;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.interfaces.request.task.AllUserRequest;
import com.tencent.sr.iris.activity.interfaces.response.group.task.AllUserRes;
import com.tencent.sr.iris.activity.service.config.MonthGiftPublishEventConfig;
import com.tencent.sr.iris.activity.service.constant.ActivityRedisKey;
import com.tencent.sr.iris.activity.service.event.handler.MonthGiftEventHandler;
import com.tencent.sr.iris.activity.service.util.IdCreateorUtil;
import com.tencent.sr.rmall.springbootstarter.elasticjob.TaskJob;
import com.tencent.sr.rmall.springbootstarter.redis.RedisClient;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import static com.tencent.sr.iris.activity.service.constant.ActivityRedisKey.ACTIVITY_MONTH_GIFT_LOCKED_KEY;

/**
 * Copyright (C) 2023 Limited, a Tencent company. All rights reserved.
 * 构建事件去触发月月领定时任务
 *
 * @author p_htaozhou@tencent.com
 * @create 2023/8/11 14:52
 */

@Slf4j
@Component
@TaskJob(jobName = "monthGiftPublishEventJob", cron = "0 0 2 * * ? ", description = "构建事件去触发月月领定时任务")
public class MonthGiftPublishEventJob implements SimpleJob {

    @Resource
    private MonthGiftPublishEventConfig monthGiftPublishEventConfig;

    @Resource
    private GroupTaskDependency groupTaskDependency;

    @Resource
    private IdCreateorUtil idCreateorUtil;

    @Resource
    private RedisClient redisClient;

    @Resource
    private DependencyConfig dependencyConfig;

    @Resource
    private MonthGiftEventHandler monthGiftEventHandler;

    @Resource(name = IrisThreadPoolConfig.IRIS_USER_BATCH_DATA_THREAD_EXECUTOR)
    private ExecutorService batchDataExecutor;

    @Resource
    private RedissonClient redissonClient;

    private static RateLimiter rateLimiter = RateLimiter.create(20);

    @Override
    public void execute(ShardingContext shardingContext) {
        log.info("【MonthGiftPublishEventJob】MonthGiftPublishEventJob定时任务执行开始");
        String redisKey = ActivityRedisKey.getIsUpdateMonthGiftKey(YearMonth.now().
                format(DateTimeFormatter.ofPattern(TimeUtils.YYYYMM)));
        boolean isUpdateMonthGift = Boolean.parseBoolean(redisClient.get(redisKey));
        if (isUpdateMonthGift) {
            log.info("【MonthGiftPublishEventJob】本月已更新处理，不再处理");
            return;
        }

        RLock lock = null;
        try {
            lock = redissonClient.getLock(ACTIVITY_MONTH_GIFT_LOCKED_KEY);
            if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                log.info("【MonthGiftPublishEventJob】本月正在处理中，本次忽略");
                return;
            }

            doExecute(redisKey);
        } catch (Exception e) {
            log.error("月月领定时任务执行失败", e);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void doExecute(String redisKey) {
        long startTime = System.currentTimeMillis();
        int pageNo = 1;
        List<AllUserRes> allUserResList;
        int totalSize = 0;

        DependencyConfig.Config queryAllTeamUserInfo = dependencyConfig.getConfigs().get("queryAllTeamUserInfo");
        // 构建查询请求参数
        AllUserRequest request = AllUserRequest.builder()
                .pageNo(pageNo)
                .pageSize(queryAllTeamUserInfo.getBatchSize())
                .build();

        // 分页查询
        while (!monthGiftPublishEventConfig.isBatchDataJobStop()) {
            if (totalSize >= monthGiftPublishEventConfig.getTotalLimit()) {
                log.info("【MonthGiftPublishEventJob】超出处理数量上限, totalLimit={}",
                        monthGiftPublishEventConfig.getTotalLimit());
                break;
            }

            // 限流处理
            if (queryAllTeamUserInfo.getPermitsPerSecond() != null) {
                rateLimiter.setRate(queryAllTeamUserInfo.getPermitsPerSecond());
                rateLimiter.acquire();
                log.info("【MonthGiftPublishEventJob】调用GroupTaskDependency.queryAllUsers，限流{}qps，请求参数：{}",
                        rateLimiter.getRate(), JSON.toJSONString(request));
            }

            //分页查询全量用户
            log.info("【MonthGiftPublishEventJob】分页查询用户req={}", JSONObject.toJSONString(request));
            allUserResList = groupTaskDependency.queryAllUsers(request).getDataList();
            int currentSize = allUserResList.size();
            log.info("【MonthGiftPublishEventJob】分页查询用户pageNo={},pageSize={},resultCount={}", pageNo,
                    queryAllTeamUserInfo.getBatchSize(), currentSize);

            if (CollectionUtils.isEmpty(allUserResList)) {
                log.info("【MonthGiftPublishEventJob】全量用户数据查询完毕");
                break;
            }

            // 组装活动事件并直接执行事件执行器
            log.info("【MonthGiftPublishEventJob】批次({})月月领事件执行器开始执行", pageNo);
            CompletableFuture.allOf(allUserResList.stream().map(allUserRes -> {
                return CompletableFuture.<Void>supplyAsync(
                        () -> {
                            try {
                                monthGiftEventHandler.execute(assembleEventDTO(allUserRes));
                            } catch (Exception e) {
                                log.error("【MonthGiftPublishEventJob】执行月月领活动事件失败:", e);
                            }
                            return null;
                        },
                        batchDataExecutor);
            }).toArray(CompletableFuture[]::new)).join();

            log.info("【MonthGiftPublishEventJob】批次({})月月领事件执行器执行结束", pageNo);
            totalSize += currentSize;
            request.setPageNo(++pageNo);
            //设置分页查询用户参数,uid参数为当页处理最大的uid
            String uid = allUserResList.get(currentSize - 1).getUid();
            request.setUid(uid);
            log.info("【MonthGiftPublishEventJob】批次({})设置下一次分页查询uid({})", pageNo, uid);
        }

        long endTime = System.currentTimeMillis();
        // 更新缓存为 已更新，设置31天过期
        redisClient.setex(redisKey, String.valueOf(Boolean.TRUE), 24 * 3600 * 31);
        log.info("【MonthGiftPublishEventJob】MonthGiftPublishEventJob定时任务执行结束，总团长数量：{}，总耗时：{} ms",
                totalSize, (endTime - startTime));
    }

    /**
     * 组装数据
     *
     * @return 事件对象
     */
    private ActivityEventDTO assembleEventDTO(AllUserRes userRes) {
        ActivityEventDTO activityEventDTO = new ActivityEventDTO();
        activityEventDTO.setIdempotent(idCreateorUtil.genId());
        activityEventDTO.setUid(userRes.getUid());
        activityEventDTO.setEventSource(ActivityEventSourceEnum.ADMIN_OPERATE.getCode());
        activityEventDTO.setCreateTime(System.currentTimeMillis());
        activityEventDTO.setEventTime(System.currentTimeMillis());
        activityEventDTO.setEventType(ActivityEventTypeEnum.MONTH_GIFT_JOB.getCode());
        return activityEventDTO;
    }
}
